using System;
using System.Globalization;

namespace Waymex.Gps.Nmea
{
	/// <summary>
	/// PGRMC NMEA propriertry sentence class.
	/// </summary>
	public class SentencePgRmc : Sentence
	{

        private PgRmcFixMode m_utFixMode;
		private double m_dblAltitude = 0.0; 
		private string m_strEarthDatum = ""; 
		private double m_dblUEDSemiMajorAxis;
		private double m_dblUEDInverseFlatteningFactor = 0.0; 
		private double m_dblUEDDeltaX = 0.0; 
		private double m_dblUEDDeltaY = 0.0; 
		private double m_dblUEDDeltaZ = 0.0; 
		private PgRmcDifferentialMode m_utDifferentialMode;
		private int m_intBaudRate = 0;
		private PgRmcFilterMode m_utFilterMode;
		private int m_intFilterTimeConstant = 0;
		private PgRmcPulseMode m_utPPSMode;
		private int m_intPulseLength = 0;   //0-48 - ms caculated by (n+1)*20
		private int m_intDeadReckoningValidTime = 0; 
		
		/// <summary>
		/// Enumeration describing the PGRMC fix mode.
		/// </summary>
		public enum PgRmcFixMode
		{
			/// <summary>
			/// Unknown.
			/// </summary>
			Unknown = 0,
			/// <summary>
			/// Automatic.
			/// </summary>
			Automatic = 1,
			/// <summary>
			/// 2D exclusive fix.
			/// </summary>
			Exclusivley2D = 2,
			/// <summary>
			/// 3D exclusive fix.
			/// </summary>
			Exclusivley3D = 3
		}
		
		/// <summary>
		/// Enumeration describing the differential mode.
		/// </summary>
		public enum PgRmcDifferentialMode
		{
			/// <summary>
			/// Unknown.
			/// </summary>
			Unknown = 0,
			/// <summary>
			/// Automatic.
			/// </summary>
			Automatic = 1,
			/// <summary>
			/// Differential.
			/// </summary>
			Differential = 2
		}

		/// <summary>
		/// Enumeration describing the PGRMC filter mode.
		/// </summary>
		public enum PgRmcFilterMode
		{
			/// <summary>
			/// Unknown.
			/// </summary>
			Unknown = 0,
			/// <summary>
			/// No filter.
			/// </summary>
			NoFilter = 1,
			/// <summary>
			/// Automatic.
			/// </summary>
			Automatic = 2,
			/// <summary>
			/// Time constant.
			/// </summary>
			TimeConstant = 3    //actual constant stored in the filter time constant property
		}
		
		/// <summary>
		/// Enumeration describing the pulse mode.
		/// </summary>
		public enum PgRmcPulseMode
		{
			/// <summary>
			/// Unknown.
			/// </summary>
			Unknown = 0,
			/// <summary>
			/// None.
			/// </summary>
			None = 1,
			/// <summary>
			/// Once per second.
			/// </summary>
			OneHertz = 2
		}

        /// <summary>
        /// Constructor for the class.
        /// </summary>
        /// <param name="sentence">NMEA sentence.</param>
        public SentencePgRmc(string sentence)
            : base(sentence)
		{
            Populate(sentence);
		}

		private void Populate(string strSentence)
		{
			int intFilterMode = 0;
			int intPulseLength = 0;
			
			//add sentence specifics here
			if(SentenceItemExists(1))
			{
				switch(m_astrSentence[1].ToUpper(CultureInfo.InvariantCulture))
				{
					case "A":
						m_utFixMode = PgRmcFixMode.Automatic;
						break;
					case "2":
						m_utFixMode = PgRmcFixMode.Exclusivley2D;
						break;
					case "3":
						m_utFixMode = PgRmcFixMode.Exclusivley3D;
						break;
					default:
						m_utFixMode = PgRmcFixMode.Unknown;
						break;
				}
			}
			else
			{
						m_utFixMode = PgRmcFixMode.Unknown;
			}

			if(SentenceItemExists(2)) m_dblAltitude = FormatNumber(m_astrSentence[2]);
			if(SentenceItemExists(3))
			{
				if(IsNumeric(m_astrSentence[3]))
				{
                    m_strEarthDatum = GetDatum(Convert.ToInt32(m_astrSentence[3], CultureInfo.InvariantCulture));
				}
			}   
			if(SentenceItemExists(4)) m_dblUEDSemiMajorAxis = FormatNumber(m_astrSentence[4]);
			if(SentenceItemExists(5)) m_dblUEDInverseFlatteningFactor = FormatNumber(m_astrSentence[5]);
			if(SentenceItemExists(6)) m_dblUEDDeltaX = FormatNumber(m_astrSentence[6]);
			if(SentenceItemExists(7)) m_dblUEDDeltaY = FormatNumber(m_astrSentence[7]);
			if(SentenceItemExists(8)) m_dblUEDDeltaZ = FormatNumber(m_astrSentence[8]);
			if(SentenceItemExists(9)) 
			{		
				switch(m_astrSentence[9].ToUpper(CultureInfo.InvariantCulture))
				{
					case "A":
						m_utDifferentialMode = PgRmcDifferentialMode.Automatic;
						break;
					case "D":
						m_utDifferentialMode = PgRmcDifferentialMode.Differential;
						break;
					default:
						m_utDifferentialMode = PgRmcDifferentialMode.Unknown;
						break;
				}
			}
			else
			{
						m_utDifferentialMode = PgRmcDifferentialMode.Unknown;
			}
			if(SentenceItemExists(10)) 
			{
				if(IsNumeric(m_astrSentence[10]))
				{
                    m_intBaudRate = Convert.ToInt32(m_astrSentence[10], CultureInfo.InvariantCulture);
				}
			}
			if(SentenceItemExists(11))
			{
				if(IsNumeric(m_astrSentence[11]))
				{
                    intFilterMode = Convert.ToInt32(m_astrSentence[11], CultureInfo.InvariantCulture);

					switch(intFilterMode)
					{
						case 0:
							m_utFilterMode = PgRmcFilterMode.NoFilter;
							break;
						case 1:
							m_utFilterMode = PgRmcFilterMode.Automatic;
							break;
						default:
				
							if(intFilterMode >= 2 && intFilterMode <= 255)
							{
								//values between 2 and 255 indicate the actual time constant
								m_utFilterMode = PgRmcFilterMode.TimeConstant;
								m_intFilterTimeConstant = intFilterMode;
							}
							else
							{
								m_utFilterMode = PgRmcFilterMode.Unknown;
							}
						
							break;
					}		
				}
				else
				{
					m_utFilterMode = PgRmcFilterMode.Unknown;
				}
			}
			else
			{
					m_utFilterMode = PgRmcFilterMode.Unknown;
			}
			if(SentenceItemExists(12))
			{
				if(IsNumeric(m_astrSentence[12]))
				{
					switch(m_astrSentence[12].ToUpper(CultureInfo.InvariantCulture))
					{
						case "1":
							m_utPPSMode = PgRmcPulseMode.None;
							break;
						case "2":
							m_utPPSMode = PgRmcPulseMode.OneHertz;
							break;
						default:
							m_utPPSMode = PgRmcPulseMode.Unknown;
							break;
					}
				}
				else
				{
					m_utPPSMode = PgRmcPulseMode.Unknown;
				}
			}
			else
			{
					m_utPPSMode = PgRmcPulseMode.Unknown;
			}
			if(SentenceItemExists(13))
			{
				if(IsNumeric(m_astrSentence[13]))
				{
                    intPulseLength = Convert.ToInt32(m_astrSentence[13], CultureInfo.InvariantCulture);
				
					//calculate the number of milliseconds
					m_intPulseLength = (intPulseLength + 1) * 20;
				}
			}
			if(SentenceItemExists(14))
			{
				if(IsNumeric(m_astrSentence[14]))
				{
                    m_intDeadReckoningValidTime = Convert.ToInt32(m_astrSentence[14], CultureInfo.InvariantCulture);
				}
			}
		}

		///<summary>
		///Returns the Fix Mode.
		///</summary>
		public PgRmcFixMode FixMode
		{
			get
			{
				return m_utFixMode;
			}
		}
		///<summary>
		///Returns the altitude in meters above/below mean-sea-level.
		///</summary>
		public double Altitude
		{
			get
			{
				return m_dblAltitude;
			}
		}
		///<summary>
		///Retruns the current Datum in use.
		///</summary>
		public string EarthDatum
		{
			get
			{
				return m_strEarthDatum;
			}
		}
		///<summary>
		///Returns the user earth datum Semi-Major Axis.
		///</summary>
		public double UedSemiMajorAxis
		{
			get
			{
				return m_dblUEDSemiMajorAxis;
			}
		}
		///<summary>
		///Returns the user earth datum Inverse Flattening Factor.
		///</summary>
		public double UedInverseFlatteningFactor
		{
			get
			{
				return m_dblUEDInverseFlatteningFactor;
			}
		}
		///<summary>
		///Returns the user earth datum Delta X.
		///</summary>
		public double UedDeltaX
		{
			get
			{
				return m_dblUEDDeltaX;
			}
		}
		///<summary>
		///Returns the user earth datum Delta Y.
		///</summary>
		public double UedDeltaY
		{
			get
			{
				return m_dblUEDDeltaY;
			}
		}
		///<summary>
		///Returns the user earth datum Delta Z.
		///</summary>
		public double UedDeltaZ
		{
			get
			{
				return m_dblUEDDeltaZ;
			}
		}
		///<summary>
		///Returns the Differential Mode.
		///</summary>
		public PgRmcDifferentialMode DifferentialMode
		{
			get
			{
				return m_utDifferentialMode;
			}
		}
		///<summary>
		///Returns the Baud Rate.
		///</summary>
		public int BaudRate
		{
			get
			{
				return m_intBaudRate;
			}
		}
		///<summary>
		///Returns the Filter Mode.
		///</summary>
		public PgRmcFilterMode FilterMode
		{
			get
			{
				return m_utFilterMode;
			}
		}
		///<summary>
		///Returns the Fileter Mode. This property is set when the PgRmcFilterMode is set
		///to TimeConstant.
		///</summary>
		public int FilterTimeConstant
		{
			get
			{
				return m_intFilterTimeConstant;
			}
		}
		///<summary>
		///Returns the Pulses Per Second mode.
		///</summary>
		public PgRmcPulseMode PpsMode
		{
			get
			{
				return m_utPPSMode;
			}
		}

		///<summary>
		///Returns the pulse length in milliseconds.
		///</summary>
		public int PulseLength
		{
			get
			{
				return m_intPulseLength;
			}
		}
		///<summary>
		///Returns the Dead Reckoning Valid Time in seconds.
		///</summary>
		public int DeadReckoningValidTime
		{
			get
			{
				return m_intDeadReckoningValidTime;
			}
		}
	}
}
